/**
 * Copyright 2019 吉鼎科技.

 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.easyplatform.services.job;

import cn.easyplatform.EntityNotFoundException;
import cn.easyplatform.cfg.EngineConfiguration;
import cn.easyplatform.contexts.Contexts;
import cn.easyplatform.contexts.EngineContext;
import cn.easyplatform.contexts.WorkflowContext;
import cn.easyplatform.dao.IdentityDao;
import cn.easyplatform.dos.EnvDo;
import cn.easyplatform.dos.FieldDo;
import cn.easyplatform.dos.UserDo;
import cn.easyplatform.entities.beans.LogicBean;
import cn.easyplatform.entities.beans.ResourceBean;
import cn.easyplatform.entities.beans.task.TaskBean;
import cn.easyplatform.entities.beans.task.Variable;
import cn.easyplatform.interceptor.CommandContext;
import cn.easyplatform.lang.Nums;
import cn.easyplatform.lang.Strings;
import cn.easyplatform.messages.vos.TaskVo;
import cn.easyplatform.services.IProjectService;
import cn.easyplatform.spi.engine.EngineFactory;
import cn.easyplatform.support.scripting.CompliableScriptEngine;
import cn.easyplatform.support.scripting.ScriptEngineFactory;
import cn.easyplatform.support.transform.Parameter;
import cn.easyplatform.support.transform.TagNode;
import cn.easyplatform.support.transform.Transformer;
import cn.easyplatform.support.transform.TransformerFactory;
import cn.easyplatform.support.transform.handler.FlatingHandler;
import cn.easyplatform.support.transform.handler.StructuringHandler;
import cn.easyplatform.type.*;
import cn.easyplatform.log.LogManager;
import cn.easyplatform.utils.Assert;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.shiro.SecurityUtils;
import org.dom4j.io.SAXReader;
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.InputStream;
import java.util.*;

/**
 * @author <a href="mailto:davidchen@epclouds.com">littleDog</a> <br/>
 * @since 2.0.0 <br/>
 */
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public class FileScanerJobWorker implements Job {

    private static final Logger log = LoggerFactory.getLogger(FileScanerJobWorker.class);

    public void execute(JobExecutionContext jc) throws JobExecutionException {
        JobDataMap dm = jc.getJobDetail().getJobDataMap();
        ResourceBean job = (ResourceBean) dm.get("job");
        EngineContext ec = (EngineContext) EngineFactory.me();
        EngineConfiguration engineConfiguration = ec.getEngineConfiguration();
        IProjectService ps = (IProjectService) engineConfiguration.getService(jc.getJobDetail().getKey().getGroup());
        CommandContext cc = new CommandContext(engineConfiguration);
        Contexts.set(CommandContext.class, cc);
        cc.setupEnv(new EnvDo(ps.getId(),
                DeviceType.JOB, ps
                .getLanguages().get(0), null, null));
        String[] extensions = null;
        String extension = job.getProps().get("extension");
        if (Strings.isBlank(extension))
            extensions = null;
        else
            extensions = extension.split(",");
        String backup = job.getProps().get("backup");
        int type = Nums.toInt(job.getProps().get("transformer"), 0);
        if (Strings.isBlank(job.getProps().get("logic")))
            Assert.notNull(job.getName(),
                    "job logic is required; it must not be empty");
        String onBefore = job.getProps().get("onBofore");
        String onAfter = job.getProps().get("onAfter");
        String path = job.getProps().get("path");
        String format = ps.getConfig().getDateFormat();
        if (path.startsWith("$707"))
            path = path.replace("$707", cc.getRealPath("$707"));
        else if (path.startsWith("$708"))
            path = path.replace("$708", cc.getRealPath("$708"));
        path = path.replace("&dt", DateFormatUtils.format(new Date(), format));
        String encoding = job.getProps().get("encoding");
        if (Strings.isBlank(encoding))
            encoding = "utf-8";
        File dir = new File(path);
        if (!dir.exists()) {
            if (log.isDebugEnabled())
                log.error("Job {},{} is not a directory", job.getId(), path);
        } else {
            Collection<File> cols = FileUtils.listFiles(dir, extensions, false);
            if (!cols.isEmpty()) {
                UserDo userInfo = null;
                try {

                    if (!Strings.isBlank(job.getProps().get("user"))) {
                        IdentityDao dao = cc.getIdentityDao(true);
                        userInfo = dao.getUser(cc.getProjectService()
                                .getConfig().getAuthenticationQuery(), job
                                .getProps().get("user"));
                    }
                    if (userInfo == null) {
                        userInfo = new UserDo();
                        userInfo.setId(job.getId());
                        userInfo.setName(job.getName());
                    }
                    userInfo.setDeviceType(DeviceType.JOB);
                    userInfo.setState(StateType.START);
                    userInfo.setIp("localhost");
                    userInfo.setLocale(cc.getEnv().getLocale());
                    if (!Strings.isBlank(job.getProps().get("org"))) {
                        userInfo.setOrg(cc.getIdentityDao(false).getUserOrg(
                                cc.getProjectService().getConfig()
                                        .getUnitQuery(),
                                job.getProps().get("org")));
                    }
                    cc.setUser(userInfo);
                    String checkHoliday = job.getProps().get("checkHoliday");
                    if (Strings.isBlank(checkHoliday))
                        checkHoliday = "false";
                    if (checkHoliday.equalsIgnoreCase("false")
                            || checkHoliday.equalsIgnoreCase("true")
                            && ps.getBusinessDateHandler(cc).isBusinessDate(
                            new Date(),
                            Currency.getInstance(ps.getLocale())
                                    .getCurrencyCode())) {
                        LogManager.startUser(engineConfiguration.getLogPath(), ps.getId(), userInfo);
                        LogManager.beginRequest(ps.getId(), userInfo);
                        TaskBean tb = new TaskBean();
                        tb.setId(job.getId());
                        tb.setName(job.getName());
                        tb.setDescription(job.getDescription());
                        List<Variable> variables = new ArrayList<Variable>();
                        //
                        Variable nameVar = new Variable();
                        nameVar.setName("FILE_NAME");
                        nameVar.setScope(ScopeType.PUBLIC);
                        nameVar.setType(FieldType.VARCHAR);
                        variables.add(nameVar);
                        //
                        Variable contentVar = new Variable();
                        contentVar.setName("FILE_CONTENT");
                        contentVar.setScope(ScopeType.PUBLIC);
                        contentVar.setType(FieldType.CLOB);
                        variables.add(contentVar);
                        tb.setVariables(variables);
                        TaskVo tv = new TaskVo("");
                        tv.setOpenModel(0);
                        tv.setRoleId("job");
                        WorkflowContext ctx = new WorkflowContext(tb, tv);
                        cc.setWorkflowContext(ctx);
                        if (type > 0) {
                            Transformer<Object, ?> transformer = null;
                            if (type == Transformer.SWI2TABLE
                                    || type == Transformer.XML2TABLE) {
                                Transformer<Object, Map<String, List<TagNode>>> trans = TransformerFactory
                                        .createTransformer(type);
                                trans.setHandler(new StructuringHandler(cc, ctx
                                        .getRecord(), extension.toUpperCase()));
                                transformer = trans;
                            } else {
                                Transformer<Object, Map<String, List<List<FieldDo>>>> trans = TransformerFactory
                                        .createTransformer(type);
                                int firstRowNum = Nums.toInt(job.getProps()
                                        .get("firstRowNum"), 0);
                                int firstColNum = Nums.toInt(job.getProps()
                                        .get("firstColNum"), 0);
                                int lastRowNum = Nums.toInt(
                                        job.getProps().get("lastRowNum"), 0);
                                int lastColNum = Nums.toInt(
                                        job.getProps().get("lastColNum"), 0);
                                trans.setHandler(new FlatingHandler(cc, ctx
                                        .getRecord(), job.getProps().get(
                                        "logic"), firstRowNum, firstColNum,
                                        lastRowNum, lastColNum));
                                transformer = trans;
                            }
                            CompliableScriptEngine engine1 = null;
                            CompliableScriptEngine engine2 = null;
                            try {
                                if (!Strings.isBlank(onBefore)) {
                                    LogicBean lb = cc.getEntity(onBefore);
                                    if (lb == null)
                                        throw new EntityNotFoundException(
                                                EntityType.LOGIC.getName(),
                                                onBefore);
                                    engine1 = ScriptEngineFactory
                                            .createCompilableEngine(cc, lb
                                                    .getContent().trim());
                                }
                                if (!Strings.isBlank(onAfter)) {
                                    LogicBean lb = cc.getEntity(onAfter);
                                    if (lb == null)
                                        throw new EntityNotFoundException(
                                                EntityType.LOGIC.getName(),
                                                onAfter);
                                    engine2 = ScriptEngineFactory
                                            .createCompilableEngine(cc, lb
                                                    .getContent().trim());
                                }
                                for (File file : cols) {
                                    if (file.isFile()) {
                                        ctx.getRecord().setValue("FILE_NAME",
                                                file.getName());
                                        if (engine1 != null)
                                            engine1.eval(ctx.getRecord());
                                        if (type == Transformer.XML2TABLE) {
                                            String content = FileUtils
                                                    .readFileToString(file,
                                                            encoding);
                                            ctx.getRecord().setValue(
                                                    "FILE_CONTENT", content);
                                            SAXReader reader = new SAXReader();
                                            InputStream is = FileUtils
                                                    .openInputStream(file);
                                            reader.setEncoding(encoding);
                                            try {
                                                transformer.transform(is, null);
                                            } finally {
                                                IOUtils.closeQuietly(is);
                                            }
                                        } else if (type == Transformer.EXCEL2TABLE) {
                                            String ext = FilenameUtils
                                                    .getExtension(file
                                                            .getName());
                                            InputStream is = FileUtils
                                                    .openInputStream(file);
                                            Parameter parameter = new Parameter();
                                            parameter.setSheets(new String[]{"1"});
                                            try {
                                                if (ext.equalsIgnoreCase("xls"))// 97~2007
                                                    transformer
                                                            .transform(new HSSFWorkbook(
                                                                    is), parameter);
                                                else
                                                    // 2010及以上
                                                    transformer
                                                            .transform(new XSSFWorkbook(
                                                                    is), parameter);
                                            } finally {
                                                IOUtils.closeQuietly(is);
                                            }
                                        } else {
                                            String content = FileUtils
                                                    .readFileToString(file,
                                                            encoding);
                                            ctx.getRecord().setValue(
                                                    "FILE_CONTENT", content);
                                            if (type == Transformer.CSV2TABLE) {
                                                String[] args = new String[2];
                                                if (Strings.isBlank(job
                                                        .getProps().get(
                                                                "rowSep")))
                                                    args[0] = "\r\n";
                                                else
                                                    args[0] = job.getProps()
                                                            .get("rowSep")
                                                            .trim();
                                                if (Strings.isBlank(job
                                                        .getProps().get(
                                                                "fieldSep")))
                                                    args[1] = ",";
                                                else
                                                    args[1] = job.getProps()
                                                            .get("fieldSep")
                                                            .trim();
                                                Parameter parameter = new Parameter();
                                                parameter.setSheets(args);
                                                transformer.transform(content,
                                                        parameter);
                                            } else
                                                transformer.transform(content, null);
                                        }
                                        if (engine2 != null)
                                            engine2.eval(ctx.getRecord());
                                        if (!Strings.isBlank(backup)) {
                                            if (backup.startsWith("$707"))
                                                backup = backup.replace("$707",
                                                        cc.getRealPath("$707"));
                                            else if (backup.startsWith("$708"))
                                                backup = backup.replace("$708",
                                                        cc.getRealPath("$708"));
                                            backup = backup
                                                    .replace(
                                                            "&dt",
                                                            DateFormatUtils
                                                                    .format(new Date(),
                                                                            format));
                                            File destDir = new File(backup);
                                            FileUtils.deleteQuietly(new File(
                                                    destDir, file.getName()));
                                            FileUtils.moveFileToDirectory(file,
                                                    new File(backup), true);
                                        }
                                    }
                                }
                            } finally {
                                if (engine1 != null)
                                    engine1.destroy();
                                if (engine2 != null)
                                    engine2.destroy();
                            }
                        } else {
                            String logicId = job.getProps().get("logic");
                            CompliableScriptEngine engine = null;
                            try {
                                LogicBean lb = cc.getEntity(logicId);
                                if (lb == null)
                                    throw new EntityNotFoundException(
                                            EntityType.LOGIC.getName(), logicId);
                                engine = ScriptEngineFactory
                                        .createCompilableEngine(cc, lb
                                                .getContent().trim());
                                for (File file : cols) {
                                    if (file.isFile()) {
                                        if (engine != null) {
                                            ctx.getRecord()
                                                    .setValue("FILE_NAME",
                                                            file.getName());
                                            ctx.getRecord().setValue(
                                                    "FILE_CONTENT",
                                                    FileUtils.readFileToString(
                                                            file, encoding));
                                            engine.eval(ctx.getRecord());
                                        }
                                        if (!Strings.isBlank(backup)) {
                                            if (backup.startsWith("$707"))
                                                backup = backup.replace("$707",
                                                        cc.getRealPath("$707"));
                                            else if (backup.startsWith("$708"))
                                                backup = backup.replace("$708",
                                                        cc.getRealPath("$708"));
                                            backup = backup
                                                    .replace(
                                                            "&dt",
                                                            DateFormatUtils
                                                                    .format(new Date(),
                                                                            format));
                                            File destDir = new File(backup);
                                            FileUtils.deleteQuietly(new File(
                                                    destDir, file.getName()));
                                            FileUtils.moveFileToDirectory(file,
                                                    new File(backup), true);
                                        }
                                    }
                                }
                            } finally {
                                engine.destroy();
                            }
                        }
                    }
                } catch (Exception ex) {
                    if (log.isErrorEnabled())
                        log.error("execute job error:{}", ex);
                } finally {
                    SecurityUtils.getSubject().logout();
                    Contexts.clear();
                    LogManager.stopUser(userInfo);
                }
            }
        }
    }
}
